package com.shenda.device.analysis;

import java.io.File;
import java.util.Comparator;
import java.util.List;

import com.shenda.api.dto.LineDTO;
import com.swak.utils.IOUtils;
import com.swak.utils.Lists;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * 高效的文件访问 -- 多文件的合并访问模式
 * 
 * @author lifeng
 * @date 2020年5月28日 上午11:41:44
 */
@Setter
@Getter
@Accessors(chain = true)
public class FileAccesser implements Comparator<File> {

	/**
	 * 数据文件
	 */
	private final File analysisFile;

	/**
	 * 文件的跨度属性, 默认500, 就是采集的频率
	 */
	private int fileSkip = 500;

	/**
	 * 文件持有
	 */
	private List<File> files;

	/**
	 * 当前第几个文件
	 */
	private int fileIndex;

	/**
	 * 当前第几行
	 */
	private int fileRow;

	/**
	 * 当前文件的位置
	 */
	private long filePosition;

	/**
	 * 当前是否使用快速访问
	 */
	private int accessMode = 1;

	/**
	 * 必要的参数
	 */
	private FileAccesser(File file) {
		this.analysisFile = file;
	}

	/**
	 * 访问文件
	 * 
	 * @param start -- 开始的帧
	 * @param size  -- 访问的帧数
	 * @param skip  -- 跳过的帧数
	 */
	public AnalysisResult doAccess(int start, int end, int size, int skip) {

		/**
		 * 是否可以使用快速访问
		 */
		boolean useFastAccess = fileSkip != 1 && skip % fileSkip == 0;

		/**
		 * 可以使用，但和之前不一致
		 */
		if ((useFastAccess && this.accessMode != fileSkip) || (!useFastAccess && this.accessMode == fileSkip)) {
			this.files = null;
			this.fileIndex = 0;
			this.filePosition = 0;
			this.fileRow = 0;
		}

		/**
		 * 设置访问模式：目前只支持两种
		 */
		this.accessMode = useFastAccess ? this.fileSkip : 1;

		/**
		 * 可以跳过的字节数 -- 如果和查询条件冲突则重置
		 */
		if (this.index2Sn(this.fileRow) >= start) {
			this.fileIndex = 0;
			this.filePosition = 0;
			this.fileRow = 0;
		}

		/**
		 * 如果跳过的帧数是文件帧数属性的倍数额可以使用快速访问文件的方式
		 */
		if (useFastAccess) {
			return this.doFastAccess(start, end, size, skip);
		}

		/**
		 * 使用全文访问文件的方式
		 */
		return this.doFullAccess(start, end, size, skip);
	}

	/**
	 * 快速访问文件
	 * 
	 * @param start
	 * @param size
	 * @param skip
	 */
	protected AnalysisResult doFastAccess(int start, int end, int size, int skip) {

		if (this.files == null) {
			/**
			 * 快速访问文件可以有多个
			 */
			File[] csvs = this.analysisFile.listFiles((dir, name) -> {
				return name.endsWith(".skip.csv");
			});

			/**
			 * 所有需要访问的文件
			 */
			List<File> files = Lists.newArrayList(csvs);

			/**
			 * 对文件排序
			 */
			Lists.sort(files, this);

			/**
			 * 持有文件
			 */
			this.files = files;
		}

		/**
		 * 返回访问的数据
		 */
		return this.doInnerAccess(files, start, end, size, skip);
	}

	/**
	 * 快速访问文件
	 * 
	 * @param start
	 * @param size
	 * @param skip
	 */
	protected AnalysisResult doFullAccess(int start, int end, int size, int skip) {

		if (this.files == null) {
			/**
			 * 快速访问文件可以有多个
			 */
			File[] csvs = this.analysisFile.listFiles((dir, name) -> {
				return name.endsWith(".all.csv");
			});

			/**
			 * 所有需要访问的文件
			 */
			List<File> files = Lists.newArrayList(csvs);

			/**
			 * 对文件排序
			 */
			Lists.sort(files, this);

			/**
			 * 持有文件
			 */
			this.files = files;
		}

		/**
		 * 返回访问的数据
		 */
		return this.doInnerAccess(files, start, end, size, skip);
	}

	/**
	 * 实际的快速访问文件
	 * 
	 * @param files -- 所有需要访问的文件
	 * @param start
	 * @param size
	 * @param skip
	 */
	protected AnalysisResult doInnerAccess(List<File> files, int start, int end, int size, int skip) {

		// 分析数据
		AnalysisResult result = new AnalysisResult().setStartFrame(start).setEndFrame(end).setStep(size);

		MultiRandomAccessFile csvVisit = null;
		try {
			csvVisit = new MultiRandomAccessFile(files, this.fileIndex);
			csvVisit.seek(this.filePosition);
			String line = null;
			int index = this.fileRow;
			int _skip = 0;
			List<LineDTO> lines = Lists.newArrayList();

			// 跳过第0行
			if (index == 0) {
				line = csvVisit.readLine();
			}

			// 让index 指向实际的行号
			while ((line = csvVisit.readLine()) != null) {

				// 行号
				index++;

				// 序号
				int sn = this.index2Sn(index);

				// 取数据的规则
				if (sn < start) {
					continue;
				}
				if (--_skip <= 0) {
					lines.add(new LineDTO().setIdx(index).setSn(sn).setData(line));
					_skip = skip / this.accessMode;
				}
				if (lines.size() >= size || (end != 0 && sn > end)) {
					break;
				}
			}

			// 保存最后访问的位置
			this.fileRow = index;
			this.filePosition = csvVisit.getFilePointer();
			this.fileIndex = csvVisit.getFileIndex();

			// 返回数据，并设置下一个开始帧
			result.setEndFrame(this.index2Sn(index + 1)).setLines(lines);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(csvVisit);
		}
		return result;
	}

	/**
	 * 文件按照顺序排列
	 */
	@Override
	public int compare(File o1, File o2) {
		return o1.getName().compareTo(o2.getName());
	}

	/**
	 * 文件序号转换为帧数
	 * 
	 * @param index -- 文件的序号
	 * @return
	 */
	private int index2Sn(int index) {
		return (index - 1) * this.accessMode + 1;
	}

	/**
	 * 优先使用旧的配置
	 * 
	 * @param old
	 * @param file
	 * @param fileSkip
	 * @return
	 */
	public static FileAccesser newFileAccesser(FileAccesser old, File file) {
		if (old != null && old.getAnalysisFile().getAbsoluteFile().equals(file.getAbsoluteFile())) {
			return old;
		}
		return new FileAccesser(file);
	}
}